#pragma once

#include "Config.h"
#include "Container.h"
#include "DBSimpleContainer.h"
#include "DBStream.h"

#include <deque>
#include <map>

namespace db
{

typedef enum {
	RC_NOCHILD			 = 0,
	RC_NAMEDCHILD		 = 1,
	RC_ARRAY			 = 2
} ERecordClass;

class Record
{
private:
    std::wstring*							    m_name;
    ERecordType									m_type;
	ERecordClass								m_class;
    Container*								    m_container;

	std::deque<Record*>*						m_child_array;
	std::map<std::wstring, Record*>*	        m_child_map;

    Record(const Record& other);
    Record& operator= (const Record& other);
public:
    typedef std::map<std::wstring, Record*>::iterator iterator;
    typedef std::map<std::wstring, Record*>::const_iterator const_iterator;
private:
    void setType(const ERecordType type) { m_type = type; }
    void setType(const ERecordType type, Container *cnt);
    void setContainer(Container *cnt) { m_container = cnt; }
    bool checkType(const ERecordType type) { return m_type == type; }
public:
	Record& operator[](const std::wstring &name);
	Record& operator[](const u32 index);
    
	const Record& operator[](const std::wstring &name) const;
	const Record& operator[](const u32 index) const;

    bool remove(const std::wstring &name, bool freeAfter = true);
    bool remove(const u32 index, bool freeAfter = true);

	const u32 getSize() const;
	void setSize(const u32 newSize);
	void setClass(const ERecordClass recordClass);
    const ERecordClass getClass() const { return m_class; }

    iterator childBegin();
    iterator childEnd();

    const_iterator childBegin() const;
    const_iterator childEnd() const;
public:
    const ERecordType getType() const { return m_type; }
    Container* getContainer() { return m_container; }
	
    const std::wstring* getName() const;
    void setName(const std::wstring& name);

    void Load(ReadStream& file); 
    void Save(WriteStream& file); 
private:
    template<class T, ERecordType ID> void Set(const T data)
    {
        if (checkType(ID))
        {
            SimpleContainer<T, ID> *cnt = dynamic_cast<SimpleContainer<T, ID>*>(m_container);
            if (cnt)
            {
               cnt->Set(data);
               return;
            }
        }
        setType(ID, new SimpleContainer<T, ID>(data));
    }
public:
    void Set(const wchar_t *data);
    void Set(const std::wstring& data);
    void Set(const void *data, u32 size);
    void Set(const bool data);
    void Set(const u32 data);
    void Set(const f32 data);
    void Set(const f64 data);

    void Clear();

    std::wstring GetAsString() const;
    bool GetAsBool() const;
    u32 GetAsInt() const;
    f32 GetAsFloat() const;
    f64 GetAsDouble() const;

	u32 GetDataSize() const;
	const void* GetAsData() const;
public:
    Record(Container *container, ERecordClass recordClass);
    Record(ERecordClass recordClass = RC_NOCHILD);
    ~Record();
};

};